#include <ws2tcpip.h>
#include <winsock2.h>
#include <mstcpip.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdio.h>
#include <windows.h>
#include "zmisc_socket.h"

int zmisc_socket_wsa_startup( void )
{
    WSADATA st_wsa_data = { 0 };  
    return WSAStartup( 0x0202, &st_wsa_data );
}

int zmisc_socket_wsa_cleanup( void )
{
    return WSACleanup( );
}

int set_socket_nonblock( int i_socket )
{
    return 0;
}


int zmisc_socket_set_reuse_addr( int i_socket )
{
    int i_reuse = 1;
    return setsockopt(i_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&i_reuse, sizeof(i_reuse));
}


int zmisc_socket_listen_tcp_ipv4( unsigned short int usi_port )
{
    int                i_socket   = -1;
    struct sockaddr_in st_addr_v4 = {0};

    i_socket = socket( AF_INET, SOCK_STREAM, 0);
    if ( 0 >= i_socket )
    {
        return -1;
    }

    zmisc_socket_set_reuse_addr( i_socket );

    memset(&st_addr_v4, 0 ,sizeof(st_addr_v4));

    st_addr_v4.sin_addr.S_un.S_addr = INADDR_ANY;
    st_addr_v4.sin_family           = AF_INET;
    st_addr_v4.sin_port             = htons( usi_port );

    if ( 0 > bind( i_socket, (const struct sockaddr *)&st_addr_v4, sizeof(st_addr_v4)) )
    {
        zmisc_socket_close( i_socket );
        return -1;
    }

    if ( 0 > listen( i_socket, SOMAXCONN) )
    {
        zmisc_socket_close( i_socket );
        return -1;
    }

    return i_socket;
}


int zmisc_socket_listen_tcp_ipv6( unsigned short int usi_port )
{
    int                 i_socket   = -1;
    struct sockaddr_in6 st_addr_V6 = { 0 };
    int                 i_opt      = 1;

    i_socket = socket( AF_INET6, SOCK_STREAM, 0);
    if ( 0 >= i_socket )
    {
        return -1;
    }

    i_opt = 1;
    if ( 0 > setsockopt( i_socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&i_opt, sizeof(i_opt))  )
    {
        zmisc_socket_close( i_socket );
        return -2;
    }

    zmisc_socket_set_reuse_addr( i_socket );

    memset(&st_addr_V6, 0 ,sizeof(st_addr_V6));

    st_addr_V6.sin6_family = AF_INET6; 
    st_addr_V6.sin6_port   = htons( usi_port ); //端口号
    st_addr_V6.sin6_addr   = in6addr_any;

    if ( 0 > bind( i_socket, (const struct sockaddr *)&st_addr_V6, sizeof(st_addr_V6)) )
    {
        zmisc_socket_close( i_socket );
        return -2;
    }

    if ( 0 > listen( i_socket, SOMAXCONN) )
    {
        zmisc_socket_close( i_socket );
        return -4;
    }

    return i_socket;
}


int zmisc_socket_close( int i_socket )
{
    if ( 0 >= i_socket )
    {
        return -1;
    }
    return closesocket( i_socket );
}


void zmisc_socket_set_keepalive(int i_socket, int i_timeout)
{
#if 0
	if (timeout) { /* timeout [s] */
		int opt = 2;

		setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &opt, sizeof(opt));

		setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout));

		opt = 3;
		setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &opt, sizeof(opt));
		opt = 1;
		setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
	}
#endif
}


void zmisc_socket_set_default_option( int i_socket )
{
    int    i_opt           = 0;
    struct linger st_liger = { 0 };

    i_opt = 0;
    setsockopt( i_socket,SOL_SOCKET,SO_DONTLINGER,(const char*)&i_opt,sizeof(i_opt));

    i_opt = 20000;
    setsockopt( i_socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&i_opt,sizeof(int));
    setsockopt( i_socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&i_opt,sizeof(int));

    st_liger.l_onoff  =1; //在closesocket()调用,但是还有数据没发送完毕的时候容许逗留,如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
    st_liger.l_linger =5; //容许逗留的时间为5秒
    setsockopt( i_socket, SOL_SOCKET, SO_LINGER, (const char*)&st_liger, sizeof(st_liger) );
}


int zmisc_socket_get_accept_ex_func( int i_socket, void** ppfn_accept_ex )
{
    DWORD         dw_bytes         = 0;
    GUID          st_AcceptEx_guid = {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}};

    return WSAIoctl( i_socket
                    , SIO_GET_EXTENSION_FUNCTION_POINTER
                    , &st_AcceptEx_guid
                    , sizeof(st_AcceptEx_guid)
                    , ppfn_accept_ex
                    , sizeof(*ppfn_accept_ex)
                    , &dw_bytes
                    , NULL
                    , NULL );
}


int zmisc_sock_set_keepalive( int i_socket, int i_timeout )
{
    int    i_opt           = 0; 

    i_opt = 1;
    if ( 0 > setsockopt( i_socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&i_opt, sizeof(i_opt)) )
    {
        printf( "[warning] setsockopt SO_KEEPALIVE\n" );
    }

    // 设置KeepAlive参数
    struct tcp_keepalive st_alive_in  = {0};
    struct tcp_keepalive st_alive_out = {0};

    st_alive_in.keepalivetime         = i_timeout * 1000;                // 开始首次KeepAlive探测前的TCP空闭时间
    st_alive_in.keepaliveinterval     = i_timeout * 1000;                // 两次KeepAlive探测间的时间间隔
    st_alive_in.onoff                 = 1;

    unsigned long ulBytesReturn = 0;
    if ( 0 > WSAIoctl(i_socket
        , SIO_KEEPALIVE_VALS
        , &st_alive_in
        , sizeof(st_alive_in)
        , &st_alive_out
        , sizeof(st_alive_out)
        , &ulBytesReturn
        , NULL
        , NULL) )
    {
        printf( "[warning] WSAIoctl SIO_KEEPALIVE_VALS\n" );
    }

    return 0; 
}


int zmisc_sock_block_recv( int i_socket, char *p_bufer, int i_len )
{
    int i_result = 0;
    int i_recved = 0;

    while ( 0 != i_len )
    {
        i_recved = recv( i_socket, p_bufer, i_len, 0 );
        if ( 0 < i_recved )
        {
            i_len    -= i_recved;
            p_bufer  += i_recved;
            i_result += i_recved;
            continue;
        }
        if ( 0 == i_recved )
        {
            i_result = -1;
            break;
        }
        if ( EINTR == errno )
        {
            continue;
        }
        i_result = -1;
        break;
    }

    return i_result;
}


int zmisc_sock_block_send( int i_socket, char *p_bufer, int i_len )
{
    int i_result = 0;
    int i_sent   = 0;

    while ( 0 != i_len )
    {
        i_sent = send( i_socket, p_bufer, i_len, 0 );
        if ( 0 < i_sent )
        {
            i_len    -= i_sent;
            p_bufer  += i_sent;
            i_result += i_sent;
            continue;
        }
        if ( 0 == i_sent )
        {
            i_result = -1;
            break;
        }
        if ( EINTR == errno )
        {
            continue;
        }
        i_result = -1;
        break;
    }

    return i_result;
}